import pandas as pd
import math

class Oper:
    def __init__(self, id, eqtype, fdt, tt):
        self.id = id
        self.eqtype = eqtype
        self.fdt = fdt
        self.tt = tt
        self.next = None
        self.switchDur = 0
        self.waitLots = set()
        self.runLots = set()
        self.move = 0
        self.qtime = 0
        self.htime = 0
        self.lastMove = 0
        self.moves = []

        if (eqtype == 'MSP'):
            self.switchDur = 5 * 60
        elif (eqtype == 'PHL'):
            self.switchDur = 20 * 60
        elif (eqtype == 'CVD'):
            self.switchDur = 360 * 60
        elif (eqtype == 'DRY'):
            self.switchDur = 60 * 60
        elif (eqtype == 'WET'):
            self.switchDur = 10 * 60
        elif (eqtype == 'STR'):
            self.switchDur = 10 * 60

class EQ:
    def __init__(self, id, eqtype):
        self.id = id
        self.type = eqtype
        self.status = 0
        self.oper = None
        self.nowProductId = 0
        self.nowVer = None
        self.availOper = {}
        self.availProduct = {}
        self.workEndTime = -1
        self.history = []

        self.switchDur = 2 * 60
        if self.type == 'PHL':
            self.switchDur = 15 * 60
        elif self.type == 'OVN':
            self.switchDur = 5 * 60

class Lot:
    def __init__(self, id, productId, ver, oper, status, glassQty, stayStartTime, level):
        self.id = id
        self.productId = productId
        self.ver = ver
        self.status = status
        self.oper = oper
        self.glassQty = glassQty
        self.stayStartTime = stayStartTime # 单位 s
        self.level = level
        self.eq = None
        self.runEndTime = -1
        self.history=[]

class Solution:

    def __init__(self):
        self.opers = {}
        self.eqs = {}
        self.lots = {}

        # 产出统计
        self.stats = []
        self.output = {}
        self.doutputArr = []
        self.doutput = {}
        self.urgentLots = set()
        self.errLots = set()
        return 

    def init(self):
        self.readFlow()
        self.readEP()
        self.readWIP()
        self.readQtime()

        return 

    def readFlow(self):
        df = pd.read_csv("Flow.csv")
        nrows = df.shape[0]
        beforeOper = None
        for i in range(0, nrows):
            row = df.iloc[i, :]
            oper = Oper(row[0], row[1], row[2] * 60, row[3])
            if beforeOper != None:
                beforeOper.next = oper
            self.opers[row[0]] = oper
            beforeOper = oper

        return
    
    def readEP(self):

        eqtypes = ['MSP', 'ITO', 'PHL', 'CVD', 'DRY', 'WET', 'STR', 'OVN']
        self.eqs = {}
        for eqtype in eqtypes:
            filename = "eq_" + eqtype + ".csv"
            df = pd.read_csv(filename)
            nrows = df.shape[0]
            ncols = df.shape[1]
            for i in range(2, ncols):
                id = df.columns[i]
                eqtype = id[:3]
                self.eqs[id] = EQ(id, eqtype)
                
                for j in range(0, nrows):
                    if df.iloc[j, i] == 'Y':
                        operId = df.iloc[j, 0]
                        productId = df.iloc[j, 1]
                        if operId not in self.eqs[id].availOper:
                            self.eqs[id].availOper[operId] = []
                        
                        if productId not in self.eqs[id].availProduct:
                            self.eqs[id].availProduct[productId] = []
                        
                        self.eqs[id].availOper[operId].append(productId)
                        self.eqs[id].availProduct[productId].append(operId)

        return
    
    def readWIP(self):
        df = pd.read_csv("wip.csv")
        nrows = df.shape[0]
        for i in range(0, nrows):    
            row = df.iloc[i, :]
            ver = None; 
            if type(row[2]) == str:
                ver = row[2] 
            status = 0
            if row[4] == 'RUN':
                status = 1
            oper = self.opers[row[3]]
            stayTime = math.ceil(row[7] * 3600)
            level = 0
            if row[8] == 1:
                level = 1
            
            lot = Lot(row[0], row[1], ver, oper, status, row[6], -stayTime, level)

            eqname = row[5]
            if (type(eqname) == str):
                eqname = eqname[2:]
                eq = self.eqs[eqname]
                eq.status = 1
                eq.oper = oper
                eq.nowProductId = row[1]
                eq.workEndTime = lot.glassQty * eq.oper.tt
                # 标记刚开始的 PHL nowVer
                if eq.type == 'PHL':
                    eq.nowVer = lot.ver
                lot.eq = eq
                lot.runEndTime = lot.oper.fdt + (lot.glassQty - 1) + lot.oper.tt
                hisdit = {"id": lot.id, "productId": lot.productId, "ver": lot.ver, "glsQty": lot.glassQty, "Oper": lot.oper.id, "Event": "Track In", "Eqp Id": lot.eq.id, "time": 0}
                lot.history.append(hisdit)
                eq.history.append({"Event": "Track in", 
                    "lotId": lot.id, 
                    "lotLevel": lot.level,
                    "oper": eq.oper.id, 
                    "productId": eq.nowProductId, 
                    "glsQty": lot.glassQty, 
                    "timeStart": 0, "timeEnd": eq.workEndTime })

            if lot.status == 0:
                oper.waitLots.add(lot)
            else:
                oper.runLots.add(lot)

            self.lots[row[0]] = lot
        
        return

    def readInput(self, idx, t = 0):
        filename = "Input_" + str(idx) + ".csv"
        df = pd.read_csv(filename)
        nrows = df.shape[0]
        firstOper = self.opers[1200]
        for i in range(0, nrows):
            row = df.iloc[i, :]
            lot = Lot(row[3], row[1], None, firstOper, 0, row[4], t, 0)
            self.lots[lot.id] = lot
            firstOper.waitLots.add(lot)
        return

    def readQtime(self):
        filename = "QTime.csv"
        df = pd.read_csv(filename)
        nrows = df.shape[0]
        for i in range(0, nrows):
            row = df.iloc[i, :]
            operId = row[0]
            self.opers[operId].qtime = row[1] * 60 * 60
            self.opers[operId].htime = row[2] * 60 * 60
        return

    def runLot(self, targetLot, eq, t):
        targetLot.status = 1
        targetLot.eq = eq
        targetLot.oper.runLots.add(targetLot)
        targetLot.runEndTime = t + targetLot.oper.fdt + targetLot.oper.tt * (targetLot.glassQty - 1)
        ######################
        hisdit = {"id":targetLot.id,"productId":targetLot.productId,"ver":targetLot.ver,"glsQty":targetLot.glassQty,"Oper": targetLot.oper.id, "Event": "Track In", "Eqp Id": targetLot.eq.id,"time": t}
        targetLot.history.append(hisdit)

        changeDur = 0
        eqOldOperId = None
        eqOldProductId = eq.nowProductId
        eqOldVer = eq.nowVer
        if eq.oper:
            eqOldOperId = eq.oper.id

        if eq.type == 'PHL':
            if targetLot.oper.id == 1300:
                # 打上版本
                targetLot.ver = 'A' + eq.id[3:4]
            else:
                if eq.nowVer != targetLot.ver:
                    changeDur = 10 * 60

        if targetLot.productId != eq.nowProductId:
            changeDur = eq.switchDur
        if targetLot.oper != eq.oper:
            changeDur = targetLot.oper.switchDur

        if eq.type == 'PHL' and targetLot.oper.id != 1300:
            eq.nowVer = targetLot.ver
        eq.status = 1
        eq.oper = targetLot.oper
        eq.nowProductId = targetLot.productId
        eq.workEndTime = t + targetLot.glassQty * eq.oper.tt + changeDur
    
        if len(eq.history) > 1:
            beforeHist = eq.history[len(eq.history) - 1]
            if beforeHist['timeEnd'] != t:
                eq.history.append({"Event": "Idle", "timeStart": beforeHist['timeEnd'], "timeEnd": t})

        if changeDur:
            eq.history.append({"Event": "Switch", 
                        "from": str(eqOldOperId) + "," + str(eqOldProductId) + "," + str(eqOldVer), 
                        "to": str(eq.oper.id) + "," + str(eq.nowProductId) + "," + str(eq.nowVer),
                        "timeStart": t, "timeEnd": t + changeDur, "timeDur": changeDur,
                        "fromOper": eqOldOperId, "fromProductId": eqOldProductId, "fromVer": eqOldVer,
                        "toOper": eq.oper.id, "toProductId": eq.nowProductId, "toVer": eq.nowVer, 
                    })
        eq.history.append({"Event": "Track in",
            "lotId": targetLot.id, 
            "lotLevel": targetLot.level,
            "oper": eq.oper.id, 
            "productId": eq.nowProductId, "ver": eq.nowVer, "glsQty": targetLot.glassQty, 
            "timeStart": t + changeDur, "timeEnd": eq.workEndTime, "timeDur": targetLot.glassQty * eq.oper.tt })
        return

    def statOpers(self):
        stat = {}
        for oper in self.opers.values():
            stat[oper.id] = {}
            stat[oper.id]['move'] = oper.move
            stat[oper.id][27500] = 0
            stat[oper.id][31500] = 0
            stat[oper.id][39000] = 0
            stat[oper.id][42500] = 0
            stat[oper.id][48500] = 0
            stat[oper.id][49500] = 0
            stat[oper.id][54600] = 0
            stat[oper.id][65500] = 0

            for lot in oper.waitLots:
                productId = lot.productId
                if productId not in stat[oper.id]:
                    stat[oper.id][productId] = 0
                stat[oper.id][productId] += lot.glassQty
            
            for lot in oper.runLots:
                productId = lot.productId
                if productId not in stat[oper.id]:
                    stat[oper.id][productId] = 0
                stat[oper.id][productId] += lot.glassQty
        self.stats.append(stat)
        df = pd.DataFrame(stat).T
        df.to_csv('test.csv')
        return 

    def printOutput(self):
        print("Output:")
        sum = 0
        for i in self.output.keys():
            print("Product ID: %d, output: %d" % (i, self.output[i]))
            sum += self.output[i]
        print("sum: %d" % sum)
        return

    def printWIPChart(self):
        i = 0
        for stat in self.stats:
            filename = 'wipchart_' + str(i) + '.csv'
            df = pd.DataFrame(stat).T
            df.to_csv(filename)
            i += 1
        return

    def printWIPChart2(self):
        i = 0
        for stat in self.stats:
            filename = 'wipchart__' + str(i) + '.csv'
            df = pd.DataFrame(stat).T
            df.to_csv(filename)
            i += 1
        return

    def printLogDetail(self):
        hist = self.lots['TA843033N00'].history
        df = pd.DataFrame(hist)
        df.to_csv('lotDetail/LOG0_Lot_TA842818N00_level1.csv')
        return

    def printLogDetail1(self):
        hist = self.lots['TA843168N00'].history
        df = pd.DataFrame(hist)
        df.to_csv('LOG1_Log__TA843168N00.csv')
        return

    def printEQdetail(self):
        hist = self.eqs['MSP100'].history
        df = pd.DataFrame(hist)
        df.to_csv('LOG0_EQ_MSP100.csv')
        return
    
    def printEQdetail1(self):
        hist = self.eqs['MSP300'].history
        df = pd.DataFrame(hist)
        df.to_csv('LOG1_EQ_MSP300.csv')
        return

    def printDoutput(self):
        i = 0
        for doutput in self.doutputArr:
            print("-------------")
            sum = 0
            for i in doutput.keys():
                print("Product ID: %d, output: %d" % (i, doutput[i]))
                sum += doutput[i]
            print("sum: %d" % sum)
        return 

    def printMoves(self):
        table = []
        for oper in self.opers.values():
            table.append({"operId": oper.id, "move_0": oper.moves[0], "move_1": oper.moves[1], "move_2": oper.moves[2]})
        df = pd.DataFrame(table)
        df.to_csv("q2_Moves.csv")
        return 

    def printErrLots(self):
        print("Err Lot Count: %d" % len(self.errLots))
        for lot in self.errLots:
            print("LogId: %s" % lot.id)

    def calMoves(self):
        for oper in self.opers.values():
            moveNew = oper.move - oper.lastMove
            oper.moves.append(moveNew)
            oper.lastMove = oper.move
        return

    def run(self):
        self.readInput(1)
        totalTime = 24 * 60 * 60
        for t in range(0, totalTime):                       
            if t in [6 * 60 * 60, 12 * 60 * 60, 18 * 60 * 60]:
                self.statOpers()

            for lot in self.urgentLots:
                if (t - lot.stayStartTime) >= lot.oper.qtime:
                    self.errLots.add(lot)

            # 查看站点中 Lot 是否都工作完
            # 转移 Lot 出当前站点，track out，并 track in 进入下一个站台
            for oper in self.opers.values():
                toremove = set()
                for lot in oper.runLots:
                    if lot.runEndTime <= t:
                        # 移出当前站点
                        # lot.oper.runLots.remove(lot)
                        toremove.add(lot)
                        eqid = lot.eq.id
                        ########################
                        lot.eq = None
                        lot.status = 0

                        # 当前 oper move 统计
                        lot.oper.move += lot.glassQty
                        lot.oper = lot.oper.next
                                            
                        # 产品完成，经过完整流水线，统计
                        if lot.oper == None:
                            if lot.productId not in self.output:
                                self.output[lot.productId] = 0
                            self.output[lot.productId] += lot.glassQty
                            continue
                        else:
                            lot.stayStartTime = t
                            lot.oper.waitLots.add(lot)

                        hisdit = {"id":lot.id, "productId": lot.productId, "ver": lot.ver, "glsQty": lot.glassQty, "Oper": lot.oper.id, 
                            "Event": "Track Out", "Eqp Id": eqid, "time": t}
                        lot.history.append(hisdit)
                if len(toremove):
                    oper.runLots.symmetric_difference_update(toremove)

                # 查看是否有紧急任务要处理
                toremove = set()
                for lot in oper.waitLots:
                    if (t - lot.stayStartTime) >= lot.oper.qtime * 0.8:
                        toremove.add(lot)
                if len(toremove):
                    oper.waitLots.symmetric_difference_update(toremove)
                    self.urgentLots.symmetric_difference_update(toremove)

            # EQ 工作策略
            # 先考虑前后处理同样的 productId
            # 在考虑其他种类的 productId
            # 可随意换站点
                        
            # 处理时
            # 在考虑先处理等级 1
            # 再考虑处理时间长的

            # 换类型处理时
            # 类型选择现在机台能处理的类型中，优先级较高，等待数量最长的产品类型
            for eq in self.eqs.values():
                if eq.status == 1 and eq.workEndTime <= t:
                    eq.status = 0

                if eq.status == 1:
                    continue

                ###########
                minTime = 99999999
                targetLot = None
                localLots = []
                localAvailProducts = []
                if eq.oper != None:
                    localLots = eq.oper.waitLots # 本站点 waitLots
                    localAvailProducts = eq.availOper[eq.oper.id] # 本站点可切换 Product

                    # 处理 qtime UrgentLot
                    # 采用首次匹配原则
                    for lot in self.urgentLots:
                        if (lot.oper.id in eq.availOper.keys()) and (lot.productId in eq.availProduct.keys()):
                            targetLot = lot
                            break
                    
                    # 处理紧急 q-time lot
                    if targetLot != None:
                        self.urgentLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                # 对于 ITO OVN 站点来说，只用考虑 1 个站点
                if eq.type == 'ITO' or eq.type == 'OVN':
                    # 处理同站点同 Product ID
                    for lot in localLots:
                        if lot.status == 0 and lot.productId == eq.nowProductId:
                            # 最后一个生产站点需要至少等待 2 小时
                            if eq.type == 'OVN' and (t - lot.stayStartTime) < 2 * 60 * 60:
                                continue
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue
                    
                    # 处理同站点不同 Product ID
                    for lot in localLots:
                        if lot.status == 0 and (lot.productId in localAvailProducts):
                            # 最后一个生产站点需要至少等待 2 小时
                            if eq.type == 'OVN' and (t - lot.stayStartTime) < 2 * 60 * 60:
                                continue
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot
                    
                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                elif eq.status != 'PHL':

                    # 处理同站点同 Product ID
                    for lot in localLots:
                        if lot.status == 0 and lot.productId == eq.nowProductId:
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue
                    
                    # 处理同站点不同 Product ID
                    for lot in localLots:
                        if lot.status == 0 and (lot.productId in localAvailProducts):
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot
                    
                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                    # 处理不同站点
                    for operId in eq.availOper.keys():
                        operAvailProduct = eq.availOper[operId]
                        for lot in self.opers[operId].waitLots:
                            if lot.status == 0 and (lot.productId in operAvailProduct):
                                score = lot.stayStartTime - lot.level * 10000000
                                if (score < minTime):
                                    minTime = score
                                    targetLot = lot
                    
                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                else:
                    # 处理同站点同 ProductId 同 ver
                    for lot in localLots:
                        if (lot.status == 0 and lot.productId == eq.nowProductId and lot.ver == eq.nowVer):
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot
                    
                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                    # 处理同站点同 ProductId 不同 ver
                    for lot in localLots:
                        if (lot.status == 0 and lot.productId == eq.nowProductId):
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                    # 处理同站点不同 ProductId
                    for lot in localLots:
                        if lot.status == 0 and (lot.productId in localAvailProducts):
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot  

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                    # 处理不同站点
                    for operId in eq.availOper.keys():
                        operAvailProduct = eq.availOper[operId]
                        for lot in self.opers[operId].waitLots:
                            if lot.status == 0 and (lot.productId in operAvailProduct):
                                score = lot.stayStartTime - lot.level * 10000000
                                if (score < minTime):
                                    minTime = score
                                    targetLot = lot   

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

        # 统计输出
        sum = 0
        for i in self.output.keys():
            sum += self.output[i]
        return sum

    def run2(self, PMInfo):
        # PMInfo[] 对应 6 具体个机台保养时间点
        # MSP300 ITO100 ITO200 WET900 WETB00 OVN200
        pmTime = {"MSP300": PMInfo[0], "ITO100": PMInfo[1], "ITO200": PMInfo[2], "WET900": PMInfo[3], "WETB00": PMInfo[4], "OVN200": PMInfo[5]}
        hasPM = {"MSP300": 0, "ITO100": 0, "ITO200": 0, "WET900": 0, "WETB00": 0, "OVN200": 0}
        pmDur = {"MSP300": 8 * 60 * 60, "ITO100": 18 * 60 * 60, "ITO200": 18 * 60 * 60, 
            "WET900": 8 * 60 * 60, "WETB00": 8 * 60 * 60, "OVN200": 10 * 60 * 60}

        totalTime = 3 * 24 * 60 * 60
        statTime = [
                0, 6 * 60 * 60, 12 * 60 * 60, 18 * 60 * 60, 
                24 * 60 * 60, 30 * 60 * 60, 36 * 60 * 60, 42 * 60 * 60, 
                48 * 60 * 60, 54 * 60 * 60, 60 * 60 * 60, 66 * 60 * 60]
        self.doutputArr = [{}, {}, {}]
        self.doutput = self.doutputArr[0]

        for t in range(0, totalTime):
            if t == 0:
                self.doutput = self.doutputArr[0]
                self.readInput(1, t)
            elif t == 1 * 24 * 60 * 60:
                self.doutput = self.doutputArr[1]
                self.readInput(2, t)
                self.calMoves()
            elif t == 2 * 24 * 60 * 60:
                self.doutput = self.doutputArr[2]
                self.readInput(3, t)
                self.calMoves()
            
            if t in statTime:
                self.statOpers()
            for oper in self.opers.values():
                toremove = set()
                for lot in oper.runLots:
                    if lot.runEndTime <= t:
                        # 移出当前站点
                        # lot.oper.runLots.remove(lot)
                        toremove.add(lot)
                        eqid = lot.eq.id
                        ########################
                        lot.eq = None
                        lot.status = 0

                        # 当前 oper move 统计
                        lot.oper.move += lot.glassQty
                        lot.oper = lot.oper.next
                                            
                        # 产品完成，经过完整流水线，统计
                        if lot.oper == None:
                            if lot.productId not in self.output:
                                self.output[lot.productId] = 0
                            self.output[lot.productId] += lot.glassQty
                            if lot.productId not in self.doutput:
                                self.doutput[lot.productId] = 0
                            self.doutput[lot.productId] += lot.glassQty            
                            continue
                        else:
                            lot.stayStartTime = t
                            lot.oper.waitLots.add(lot)

                        hisdit = {"id":lot.id, "productId": lot.productId, "ver": lot.ver, "glsQty": lot.glassQty, "Oper": lot.oper.id, 
                            "Event": "Track Out", "Eqp Id": eqid, "time": t}
                        lot.history.append(hisdit)
                if len(toremove):
                    oper.runLots.symmetric_difference_update(toremove)

                # for lot in oper.waitLots:
                #     if (t - lot.stayStartTime) >= lot.oper.qtime:
                #         self.errLots.add(lot)
                
            for eq in self.eqs.values():
                if eq.status == 1 and eq.workEndTime <= t:
                    eq.status = 0
                    # 进行保养
                    if eq.id in hasPM:
                        if hasPM[eq.id] == 0 and pmTime[eq.id] <= t:
                            eq.status = 1
                            eq.workEndTime = t + pmDur[eq.id]
                            hasPM[eq.id] = 1
                            eq.history.append({"Event": "PM", "timeStart": t, "timeEnd": eq.workEndTime})
                if eq.status == 1:
                    continue

                ###########
                minTime = 99999999
                targetLot = None
                localLots = []
                localAvailProducts = []
                if eq.oper != None:
                    localLots = eq.oper.waitLots # 本站点 waitLots
                    localAvailProducts = eq.availOper[eq.oper.id] # 本站点可切换 Product

                # 对于 ITO OVN 站点来说，只用考虑 1 个站点
                if eq.type == 'ITO' or eq.type == 'OVN':
                    # 处理同站点同 Product ID
                    for lot in localLots:
                        if lot.status == 0 and lot.productId == eq.nowProductId:
                            # 最后一个生产站点需要至少等待 2 小时
                            if eq.type == 'OVN' and (t - lot.stayStartTime) < 2 * 60 * 60:
                                continue
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue
                    
                    # 处理同站点不同 Product ID
                    for lot in localLots:
                        if lot.status == 0 and (lot.productId in localAvailProducts):
                            # 最后一个生产站点需要至少等待 2 小时
                            if eq.type == 'OVN' and (t - lot.stayStartTime) < 2 * 60 * 60:
                                continue
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot
                    
                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                elif eq.status != 'PHL':

                    # 处理同站点同 Product ID
                    for lot in localLots:
                        if lot.status == 0 and lot.productId == eq.nowProductId:
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue
                    
                    # 处理同站点不同 Product ID
                    for lot in localLots:
                        if lot.status == 0 and (lot.productId in localAvailProducts):
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot
                    
                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                    # 处理不同站点
                    for operId in eq.availOper.keys():
                        operAvailProduct = eq.availOper[operId]
                        for lot in self.opers[operId].waitLots:
                            if lot.status == 0 and (lot.productId in operAvailProduct):
                                score = lot.stayStartTime - lot.level * 10000000
                                if (score < minTime):
                                    minTime = score
                                    targetLot = lot
                    
                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                else:
                    # 处理同站点同 ProductId 同 ver
                    for lot in localLots:
                        if (lot.status == 0 and lot.productId == eq.nowProductId and lot.ver == eq.nowVer):
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot
                    
                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                    # 处理同站点同 ProductId 不同 ver
                    for lot in localLots:
                        if (lot.status == 0 and lot.productId == eq.nowProductId):
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                    # 处理同站点不同 ProductId
                    for lot in localLots:
                        if lot.status == 0 and (lot.productId in localAvailProducts):
                            score = lot.stayStartTime - lot.level * 10000000
                            if (score < minTime):
                                minTime = score
                                targetLot = lot  

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue

                    # 处理不同站点
                    for operId in eq.availOper.keys():
                        operAvailProduct = eq.availOper[operId]
                        for lot in self.opers[operId].waitLots:
                            if lot.status == 0 and (lot.productId in operAvailProduct):
                                score = lot.stayStartTime - lot.level * 10000000
                                if (score < minTime):
                                    minTime = score
                                    targetLot = lot   

                    if targetLot != None:
                        targetLot.oper.waitLots.remove(targetLot)
                        self.runLot(targetLot, eq, t)
                        continue
        
        self.calMoves()
        # 统计输出
        sum = 0
        for i in self.output.keys():
            sum += self.output[i]
        return sum
